package com.shuo.framework.context;

import com.shuo.framework.annotation.GGAutowired;
import com.shuo.framework.annotation.GGController;
import com.shuo.framework.annotation.GGService;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Created by shuoGG on 2018/9/12 0012
 */
public class GGApplicationContext {
    // key:beanId  value: 加了@GGController@GGService的类的实例
    private Map<String, Object> instanceMapping = new ConcurrentHashMap<>();
    // 扫描指定的package下所有类的全类名
    private List<String> classCache = new ArrayList<>();
    private Properties config = new Properties();

    public GGApplicationContext(String location) {
        InputStream is;
        try {
            is = this.getClass().getClassLoader().getResourceAsStream(location);
            config.load(is);
            String packageToScan = config.getProperty("scanPackage");
            doRegister(packageToScan);
            doCreateBean();
            populate();
            System.out.println("IOC容器初始化完成");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 扫描指定包下的所有类并将全类名add到classCache
     *
     * @param packageName
     */
    private void doRegister(String packageName) {
        // url为运行环境下packageName的绝对路径 (包的文件夹的绝对路径)
        URL url = this.getClass().getClassLoader().getResource("/" + packageName.replaceAll("\\.", "/"));
        assert url != null;
        File dir = new File(url.getFile());
        for (File f : dir.listFiles()) {
            if (f.isDirectory())
                doRegister(packageName + "." + f.getName());
            else
                classCache.add(packageName + "." + f.getName().replace(".class", "").trim());
        }
    }

    /**
     * 将加了@Controller @Service @Component的类都instanceMapping.put(beanId, 实例)
     */
    private void doCreateBean() {
        if (classCache.size() == 0) return;
        try {
            for (String className : classCache) {
                Class<?> clazz = Class.forName(className);
                if (clazz.isAnnotationPresent(GGController.class)) {
                    String beanId;
                    GGController controller = clazz.getAnnotation(GGController.class);
                    if ("".equals(controller.value().trim())) {
                        beanId = lowerFirstChar(clazz.getSimpleName());
                    } else {
                        beanId = controller.value();
                    }
                    instanceMapping.put(beanId, clazz.newInstance());
                } else if (clazz.isAnnotationPresent(GGService.class)) {
                    GGService service = clazz.getAnnotation(GGService.class);
                    if ("".equals(service.value().trim())) {
                        Class<?>[] interfaces = clazz.getInterfaces();
                        for (Class<?> selfInterface : interfaces) {
                            instanceMapping.put(selfInterface.getName(), clazz.newInstance());
                        }
                    } else {
                        instanceMapping.put(service.value(), clazz.newInstance());
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 遍历所有带@Controller @Service @Component的类
     * 将这些类里面所有@Autowired的Field注入实例
     */
    private void populate() {
        if (instanceMapping.isEmpty()) return;
        for (Map.Entry<String, Object> entry : instanceMapping.entrySet()) {
            Field[] fields = entry.getValue().getClass().getDeclaredFields();
            for (Field field : fields) {
                if (field.isAnnotationPresent(GGAutowired.class)) {
                    GGAutowired autowired = field.getAnnotation(GGAutowired.class);
                    String beanId = autowired.value().trim();
                    if ("".equals(beanId)) {
                        // 为空则用类名
                        beanId = field.getType().getName();
                    }
                    field.setAccessible(true);
                    try {
                        field.set(entry.getValue(), instanceMapping.get(beanId));
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                }
            }
        }

    }

    private String lowerFirstChar(String str) {
        char[] chars = str.toCharArray();
        chars[0] += 32;
        return String.valueOf(chars);
    }

    public Map<String,Object> getAll(){
        return instanceMapping;
    }


    public Properties getConfig() {
        return config;
    }
}
